home *** CD-ROM | disk | FTP | other *** search
/ MacFormat 1995 January / macformat-020.iso / Shareware City / Developers / OutOfPhase1.01Source / OutOfPhase Folder / Level 0 Macintosh 07Aug94 / Menus.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-10-01  |  15.4 KB  |  504 lines  |  [TEXT/KAHL]

  1. /* Menus.c */
  2. /*****************************************************************************/
  3. /*                                                                           */
  4. /*    System Dependency Library for Building Portable Software               */
  5. /*    Macintosh Version                                                      */
  6. /*    Written by Thomas R. Lawrence, 1993 - 1994.                            */
  7. /*                                                                           */
  8. /*    This file is Public Domain; it may be used for any purpose whatsoever  */
  9. /*    without restriction.                                                   */
  10. /*                                                                           */
  11. /*    This package is distributed in the hope that it will be useful,        */
  12. /*    but WITHOUT ANY WARRANTY; without even the implied warranty of         */
  13. /*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                   */
  14. /*                                                                           */
  15. /*    Thomas R. Lawrence can be reached at tomlaw@world.std.com.             */
  16. /*                                                                           */
  17. /*****************************************************************************/
  18.  
  19. #include "MiscInfo.h"
  20. #include "Debug.h"
  21. #include "Audit.h"
  22. #include "Definitions.h"
  23.  
  24. #pragma options(pack_enums)
  25. #include <Menus.h>
  26. #include <Fonts.h>
  27. #include <Desk.h>
  28. #pragma options(!pack_enums)
  29.  
  30. #include "Menus.h"
  31. #include "Memory.h"
  32. #include "Array.h"
  33.  
  34.  
  35. /* structure represents a menu */
  36. struct MenuType
  37.     {
  38.         /* menu resource ID number (must be unique) */
  39.         short                    MenuID;
  40.         /* handle pointing to system's idea of what the menu is */
  41.         MenuHandle        DaMenuHandle;
  42.     };
  43.  
  44.  
  45. /* structure represents a menu item (it remembers the menu and index of item) */
  46. struct MenuItemType
  47.     {
  48.         /* Menu ID + Item Index number used by menu manager to identify item */
  49.         long                    MenuManagerID;
  50.         /* pointer to the menu record for the menu that contains the item. */
  51.         MenuType*            WhatMenu;
  52.     };
  53.  
  54.  
  55. /* list of menus (for finding unique menu IDs) */
  56. static ArrayRec*                    OurMenuList = NIL;
  57.  
  58. /* list of items in the menus (for searching during menu-choice events) */
  59. static ArrayRec*                    OurItemList = NIL;
  60.  
  61. /* the Apple menu record.  NIL means there is no Apple menu */
  62. static MenuType*                    AppleMenu = NIL;
  63.  
  64. /* number of "real" items on the apple menu.  This is set to 0 whenever the */
  65. /* apple menu is created. */
  66. static long                                NumAppleMenuItems;
  67.  
  68. /* flag indicating that menu bar needs to be redrawn */
  69. static MyBoolean                    RedrawMenuBarFlag = False;
  70.  
  71. /* debugging flag */
  72. EXECUTE(static MyBoolean    Initialized = False;)
  73.  
  74.  
  75. /* Initialize the menu subsystem.  This must be called before any menu routines */
  76. /* are used.  It is local to Level 0 and called from module Screen (InitializeScreen) */
  77. /* and should not be called from anywhere else. */
  78. MyBoolean                            Eep_InitializeMenus(void)
  79.     {
  80.         ERROR(Initialized,PRERR(ForceAbort,"InitializeMenus called more than once"));
  81.         /* create list that contains menus */
  82.         OurMenuList = NewArray();
  83.         if (OurMenuList == NIL)
  84.             {
  85.              FailurePoint1:
  86.                 return False;
  87.             }
  88.         /* create the list that will hold all of the menu item records (for searching) */
  89.         OurItemList = NewArray();
  90.         if (OurItemList == NIL)
  91.             {
  92.              FailurePoint2:
  93.                 DisposeArray(OurMenuList);
  94.                 goto FailurePoint1;
  95.             }
  96.         /* initialize the apple menu variable to indicate that there isn't one */
  97.         AppleMenu = NIL;
  98.         EXECUTE(Initialized = True);
  99.         return True;
  100.     }
  101.  
  102.  
  103. /* Destroy any menu stuff that needs to be cleaned up before the program quits. */
  104. /* this should not be called from anywhere else except ShutdownScreen */
  105. void                                    Eep_ShutdownMenus(void)
  106.     {
  107.         ERROR(!Initialized,PRERR(ForceAbort,
  108.             "ShutdownMenus:  Menu manager hasn't been initialized"));
  109.         ERROR(ArrayGetLength(OurMenuList) != 0,PRERR(AllowResume,
  110.             "Eep_ShutdownMenus:  still some menus in existence"));
  111.         ERROR(ArrayGetLength(OurItemList) != 0,PRERR(AllowResume,
  112.             "Eep_ShutdownMenus:  still some menu items in existence"));
  113.         DisposeArray(OurMenuList);
  114.         DisposeArray(OurItemList);
  115.     }
  116.  
  117.  
  118. /* create an implementation defined "utility" menu.  On the Macintosh, this is */
  119. /* the standard "Apple Menu". */
  120. MenuType*                            MakeAppleMenu(void)
  121.     {
  122.         ERROR(!Initialized,PRERR(ForceAbort,"Menu manager hasn't been initialized"));
  123.         /* if an apple menu exists, we return it instead of creating a new one */
  124.         if (AppleMenu == NIL)
  125.             {
  126.                 /* create a new menu normally with an apple character */
  127.                 AppleMenu = MakeNewMenu("\024");
  128.                 if (AppleMenu == NIL)
  129.                     {
  130.                         return NIL;
  131.                     }
  132.                 /* initialize item count */
  133.                 NumAppleMenuItems = 0;
  134.                 /* append the line and desk accessory list */
  135.                 AppendMenu(AppleMenu->DaMenuHandle,"\p(-");
  136.                 AddResMenu(AppleMenu->DaMenuHandle,'DRVR');
  137.             }
  138.         CheckPtrExistence(AppleMenu);
  139.         return AppleMenu;
  140.     }
  141.  
  142.  
  143. /* create a new menu with the specified name.  The menu will not */
  144. /* be displayed on the menu bar */
  145. MenuType*                            MakeNewMenu(char* MenuName)
  146.     {
  147.         unsigned char                NameTemp[256];
  148.         long                                Scan;
  149.         long                                Limit;
  150.         MenuType*                        Menu;
  151.  
  152.         ERROR(!Initialized,PRERR(ForceAbort,"Menu manager hasn't been initialized"));
  153.         /* allocate the menu record */
  154.         Menu = (MenuType*)AllocPtrCanFail(sizeof(MenuType),"MenuType");
  155.         if (Menu == NIL)
  156.             {
  157.              FailurePoint1:
  158.                 return NIL;
  159.             }
  160.         /* convert name to a pascal string */
  161.         Scan = 0;
  162.         while ((Scan < 255) && (MenuName[Scan] != 0))
  163.             {
  164.                 NameTemp[Scan + 1] = MenuName[Scan];
  165.                 Scan += 1;
  166.             }
  167.         NameTemp[0] = Scan;
  168.         /* find an unused ID number */
  169.         Limit = ArrayGetLength(OurMenuList);
  170.         Menu->MenuID = 256; /* skip 0..255 which are reserved for hierarchical menus */
  171.      LoopPoint:
  172.         for (Scan = 0; Scan < Limit; Scan += 1)
  173.             {
  174.                 if (Menu->MenuID == ((MenuType*)ArrayGetElement(OurMenuList,Scan))->MenuID)
  175.                     {
  176.                         Menu->MenuID += 1;
  177.                         goto LoopPoint;
  178.                     }
  179.                 ERROR(Scan > 16383,PRERR(ForceAbort,"MakeNewMenu:  out of menu IDs"));
  180.             }
  181.         /* allocate the menu itself */
  182.         Menu->DaMenuHandle = NewMenu(Menu->MenuID,NameTemp);
  183.         if (Menu->DaMenuHandle == NIL)
  184.             {
  185.              FailurePoint2:
  186.                 ReleasePtr((char*)Menu);
  187.                 goto FailurePoint1;
  188.             }
  189.         /* add menu to list */
  190.         if (!ArrayAppendElement(OurMenuList,Menu))
  191.             {
  192.              FailurePoint3:
  193.                 DisposeMenu(Menu->DaMenuHandle);
  194.                 goto FailurePoint2;
  195.             }
  196.         return Menu;
  197.     }
  198.  
  199.  
  200. /* hide a menu if it's on the menu bar and delete it and all of the items */
  201. /* it contains. */
  202. void                                    KillMenuAndDeleteItems(MenuType* TheMenu)
  203.     {
  204.         long                                Scan;
  205.         long                                Limit;
  206.  
  207.         ERROR(!Initialized,PRERR(ForceAbort,"Menu manager hasn't been initialized"));
  208.         CheckPtrExistence(TheMenu);
  209.         /* remove menu from our list of menus */
  210.         ArrayDeleteElement(OurMenuList,ArrayFindElement(OurMenuList,TheMenu));
  211.         /* remove menu from the menu bar */
  212.         HideMenu(TheMenu);
  213.         /* dispose the system menu block */
  214.         DisposeMenu(TheMenu->DaMenuHandle);
  215.         /* reset the apple menu value if we're disposing that */
  216.         if (TheMenu == AppleMenu)
  217.             {
  218.                 AppleMenu = NIL;
  219.             }
  220.         /* dispose all translation entries for the menu */
  221.         Limit = ArrayGetLength(OurItemList);
  222.         Scan = 0;
  223.         while (Scan < Limit)
  224.             {
  225.                 MenuItemType*            MenuItem;
  226.  
  227.                 MenuItem = (MenuItemType*)ArrayGetElement(OurItemList,Scan);
  228.                 if ((MenuItem->MenuManagerID >> 16) == TheMenu->MenuID)
  229.                     {
  230.                         /* dispose the item */
  231.                         ArrayDeleteElement(OurItemList,Scan);
  232.                         ReleasePtr((char*)MenuItem);
  233.                         /* since there's one less item now, decrement limit */
  234.                         Limit -= 1;
  235.                         /* note that we don't increment scan */
  236.                     }
  237.                  else
  238.                     {
  239.                         /* scan is only incremented if we didn't delete the element */
  240.                         Scan += 1;
  241.                     }
  242.             }
  243.         /* finally, delete the menu record */
  244.         ReleasePtr((char*)TheMenu);
  245.     }
  246.  
  247.  
  248. /* post a menu to the menu bar if it isn't already there */
  249. void                                    ShowMenu(MenuType* TheMenu)
  250.     {
  251.         ERROR(!Initialized,PRERR(ForceAbort,"Menu manager hasn't been initialized"));
  252.         CheckPtrExistence(TheMenu);
  253.         /* append menu to menu bar */
  254.         InsertMenu(TheMenu->DaMenuHandle,0);
  255.         /* set flag to indicate that the menu bar needs to be redrawn */
  256.         RedrawMenuBarFlag = True;
  257.     }
  258.  
  259.  
  260. /* remove a menu from the menu bar if it is there */
  261. void                                    HideMenu(MenuType* TheMenu)
  262.     {
  263.         ERROR(!Initialized,PRERR(ForceAbort,"Menu manager hasn't been initialized"));
  264.         CheckPtrExistence(TheMenu);
  265.         /* remove menu from menu bar */
  266.         DeleteMenu(TheMenu->MenuID);
  267.         /* set flag to indicate that the menu bar needs to be redrawn */
  268.         RedrawMenuBarFlag = True;
  269.     }
  270.  
  271.  
  272. /* append a new item to an existing menu.  The Shortcut specifies a key that */
  273. /* can be used instead of pulling down the menu.  How this is done and which */
  274. /* keys are allowed are implementation defined.  On the Macintosh, the Command */
  275. /* key is used; keys should be numbers or upper case letters.  If two menu items */
  276. /* are specified with the same shortcut, the result is undefined. */
  277. /* by default, the item is greyed out (disabled). */
  278. MenuItemType*                    MakeNewMenuItem(MenuType* TheMenu, char* MenuItemName,
  279.                                                 char Shortcut)
  280.     {
  281.         unsigned char                NameTemp[256];
  282.         long                                Scan;
  283.         MenuItemType*                MenuItem;
  284.         short                                Index;
  285.  
  286.         ERROR(!Initialized,PRERR(ForceAbort,"Menu manager hasn't been initialized"));
  287.         CheckPtrExistence(TheMenu);
  288.         /* allocate record */
  289.         MenuItem = (MenuItemType*)AllocPtrCanFail(sizeof(MenuItemType),"MenuItemType");
  290.         if (MenuItem == NIL)
  291.             {
  292.              FailurePoint1:
  293.                 return NIL;
  294.             }
  295.         /* add item to the item list */
  296.         if (!ArrayAppendElement(OurItemList,MenuItem))
  297.             {
  298.              FailurePoint2:
  299.                 ReleasePtr((char*)MenuItem);
  300.                 goto FailurePoint1;
  301.             }
  302.         /* find out where on menu the item should be placed */
  303.         if (TheMenu == AppleMenu)
  304.             {
  305.                 Index = NumAppleMenuItems + 1;
  306.                 NumAppleMenuItems += 1;
  307.             }
  308.          else
  309.             {
  310.                 Index = CountMItems(TheMenu->DaMenuHandle) + 1;
  311.             }
  312.         /* insert a dummy item (name will be changed) */
  313.         InsMenuItem(TheMenu->DaMenuHandle,"\px",Index - 1);
  314.         /* convert name */
  315.         Scan = 0;
  316.         while ((Scan < 255) && (MenuItemName[Scan] != 0))
  317.             {
  318.                 NameTemp[Scan + 1] = MenuItemName[Scan];
  319.                 Scan += 1;
  320.             }
  321.         NameTemp[0] = Scan;
  322.         /* set the name properly */
  323.         SetItem(TheMenu->DaMenuHandle,Index,NameTemp);
  324.         SetItemCmd(TheMenu->DaMenuHandle,Index,Shortcut);
  325.         /* set item's ID mapping */
  326.         MenuItem->MenuManagerID = ((long)(TheMenu->MenuID) << 16) | Index;
  327.         MenuItem->WhatMenu = TheMenu;
  328.         return MenuItem;
  329.     }
  330.  
  331.  
  332. /* delete the specified item from the menu. */
  333. void                                    KillMenuItem(MenuItemType* TheItem)
  334.     {
  335.         long                            Scan;
  336.         long                            Limit;
  337.  
  338.         ERROR(!Initialized,PRERR(ForceAbort,"Menu manager hasn't been initialized"));
  339.         CheckPtrExistence(TheItem);
  340.         /* remove item from item list */
  341.         ArrayDeleteElement(OurItemList,ArrayFindElement(OurItemList,TheItem));
  342.         /* delete item from actual menu */
  343.         DelMenuItem(TheItem->WhatMenu->DaMenuHandle,TheItem->MenuManagerID & 0xffff);
  344.         /* fix up item IDs for items after this one on the same menu */
  345.         Limit = ArrayGetLength(OurItemList);
  346.         for (Scan = 0; Scan < Limit; Scan += 1)
  347.             {
  348.                 MenuItemType*            OtherItem;
  349.  
  350.                 OtherItem = (MenuItemType*)ArrayGetElement(OurItemList,Scan);
  351.                 if (((TheItem->MenuManagerID >> 16) == (OtherItem->MenuManagerID >> 16))
  352.                     && ((TheItem->MenuManagerID & 0xffff) < (OtherItem->MenuManagerID & 0xffff)))
  353.                     {
  354.                         /* all items after this one move up.  We don't have to do masking and */
  355.                         /* all that because item IDs (low 16 bits) are positive, so a borrow */
  356.                         /* will never occur */
  357.                         OtherItem->MenuManagerID -= 1;
  358.                     }
  359.             }
  360.         /* fix up apple menu length */
  361.         if (TheItem->WhatMenu == AppleMenu)
  362.             {
  363.                 NumAppleMenuItems -= 1;
  364.             }
  365.         /* release the memory */
  366.         ReleasePtr((char*)TheItem);
  367.     }
  368.  
  369.  
  370. /* enable a menu item. Items may only be selected if enabled. */
  371. void                                    EnableMenuItem(MenuItemType* TheItem)
  372.     {
  373.         ERROR(!Initialized,PRERR(ForceAbort,"Menu manager hasn't been initialized"));
  374.         CheckPtrExistence(TheItem);
  375.         EnableItem(TheItem->WhatMenu->DaMenuHandle,TheItem->MenuManagerID & 0xffff);
  376.     }
  377.  
  378.  
  379. /* disable a menu item. */
  380. void                                    DisableMenuItem(MenuItemType* TheItem)
  381.     {
  382.         ERROR(!Initialized,PRERR(ForceAbort,"Menu manager hasn't been initialized"));
  383.         CheckPtrExistence(TheItem);
  384.         DisableItem(TheItem->WhatMenu->DaMenuHandle,TheItem->MenuManagerID & 0xffff);
  385.     }
  386.  
  387.  
  388. /* Set an implementation defined mark to indicate that the menu item has been */
  389. /* persistently selected.  On the Macintosh, this places a checkmark to the left */
  390. /* of the name of the menu item */
  391. void                                    SetItemCheckmark(MenuItemType* TheItem)
  392.     {
  393.         ERROR(!Initialized,PRERR(ForceAbort,"Menu manager hasn't been initialized"));
  394.         CheckPtrExistence(TheItem);
  395.         SetItemMark(TheItem->WhatMenu->DaMenuHandle,TheItem->MenuManagerID & 0xffff,checkMark);
  396.     }
  397.  
  398.  
  399. /* remove the implementation defined mark */
  400. void                                    ClearItemCheckmark(MenuItemType* TheItem)
  401.     {
  402.         ERROR(!Initialized,PRERR(ForceAbort,"Menu manager hasn't been initialized"));
  403.         CheckPtrExistence(TheItem);
  404.         SetItemMark(TheItem->WhatMenu->DaMenuHandle,TheItem->MenuManagerID & 0xffff,noMark);
  405.     }
  406.  
  407.  
  408. /* change the name of a menu item */
  409. void                                    ChangeItemName(MenuItemType* TheItem, char* NewName)
  410.     {
  411.         unsigned char                NameTemp[256];
  412.         long                                Scan;
  413.  
  414.         ERROR(!Initialized,PRERR(ForceAbort,"Menu manager hasn't been initialized"));
  415.         CheckPtrExistence(TheItem);
  416.         Scan = 0;
  417.         while ((Scan < 255) && (NewName[Scan] != 0))
  418.             {
  419.                 NameTemp[Scan + 1] = NewName[Scan];
  420.                 Scan += 1;
  421.             }
  422.         NameTemp[0] = Scan;
  423.         SetItem(TheItem->WhatMenu->DaMenuHandle,TheItem->MenuManagerID & 0xffff,NameTemp);
  424.     }
  425.  
  426.  
  427. /* Add an implementation defined "separator" to the end of the menu.  On the */
  428. /* Macintosh, this separator is a grey line. */
  429. void                                    AppendSeparator(MenuType* TheMenu)
  430.     {
  431.         ERROR(!Initialized,PRERR(ForceAbort,"Menu manager hasn't been initialized"));
  432.         CheckPtrExistence(TheMenu);
  433.         AppendMenu(TheMenu->DaMenuHandle,"\p(-");
  434.     }
  435.  
  436.  
  437. /* Disable all menu items, remove any checkmarks */
  438. void                                    WipeMenusClean(void)
  439.     {
  440.         long                                Scan;
  441.         long                                Limit;
  442.  
  443.         ERROR(!Initialized,PRERR(ForceAbort,"Menu manager hasn't been initialized"));
  444.         Limit = ArrayGetLength(OurItemList);
  445.         for (Scan = 0; Scan < Limit; Scan += 1)
  446.             {
  447.                 MenuItemType*                Item;
  448.  
  449.                 Item = (MenuItemType*)ArrayGetElement(OurItemList,Scan);
  450.                 DisableMenuItem(Item);
  451.                 ClearItemCheckmark(Item);
  452.             }
  453.     }
  454.  
  455.  
  456. /* internal routine for converting the number returned from the Toolbox into */
  457. /* one of our own ID numbers.  if the MMID number is an apple menu item, then */
  458. /* it is handled here and NIL is returned. */
  459. MenuItemType*                    Eep_MMID2ItemID(long MMID)
  460.     {
  461.         ERROR(!Initialized,PRERR(ForceAbort,"Menu manager hasn't been initialized"));
  462.         if ((AppleMenu != NIL) && ((MMID >> 16) == AppleMenu->MenuID)
  463.             && ((MMID & 0xffff) > NumAppleMenuItems))
  464.             {
  465.                 Str255                            DeskAccName;
  466.  
  467.                 GetItem(AppleMenu->DaMenuHandle,MMID & 0xffff,DeskAccName);
  468.                 APRINT(("*OpenDeskAcc %p",DeskAccName));
  469.                 OpenDeskAcc(DeskAccName);
  470.                 HiliteMenu(0);
  471.             }
  472.          else
  473.             {
  474.                 long                                Scan;
  475.                 long                                Limit;
  476.  
  477.                 Limit = ArrayGetLength(OurItemList);
  478.                 for (Scan = 0; Scan < Limit; Scan += 1)
  479.                     {
  480.                         MenuItemType*                Item;
  481.  
  482.                         Item = (MenuItemType*)ArrayGetElement(OurItemList,Scan);
  483.                         if (Item->MenuManagerID == MMID)
  484.                             {
  485.                                 return Item;
  486.                             }
  487.                     }
  488.             }
  489.         /* fall through == no item. */
  490.         return NIL;
  491.     }
  492.  
  493.  
  494. /* this is called from the event loop to redraw the menu bar if it needs it. */
  495. /* redraws are deferred and unified to save time. */
  496. void                                    Eep_RedrawMenuBar(void)
  497.     {
  498.         if (RedrawMenuBarFlag)
  499.             {
  500.                 DrawMenuBar();
  501.                 RedrawMenuBarFlag = False;
  502.             }
  503.     }
  504.